package com.gxar.quick.ar.server.db.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.gxar.common.entity.PageResult;
import com.gxar.common.entity.Result;
import com.gxar.common.entity.SlippageResult;
import com.gxar.common.entity.login.UserProfile;
import com.gxar.common.exception.APIRuntimeException;
import com.gxar.common.utils.PageUtils;
import com.gxar.common.utils.SnowFlakeUtils;
import com.gxar.fusion.redis.annotations.RedissonLock;
import com.gxar.fusion.redis.api.lock.FusionRedissonLock;
import com.gxar.fusion.redis.service.RedisService;
import com.gxar.quick.ar.server.base.common.constant.IdentifyConstant;
import com.gxar.quick.ar.server.base.common.constant.RedisConstant;
import com.gxar.quick.ar.server.base.common.constant.WorksConstant;
import com.gxar.quick.ar.server.base.common.enums.ComboConfigItemEnum;
import com.gxar.quick.ar.server.base.common.enums.DeletedEnum;
import com.gxar.quick.ar.server.base.common.enums.UserWorksSourceEnum;
import com.gxar.quick.ar.server.base.common.enums.UserWorksTypeEnum;
import com.gxar.quick.ar.server.base.common.enums.WorksShowsTypeEnum;
import com.gxar.quick.ar.server.base.common.enums.WorksStatusEnum;
import com.gxar.quick.ar.server.base.common.properties.IdentifyProperties;
import com.gxar.quick.ar.server.base.common.enums.*;
import com.gxar.quick.ar.server.base.common.properties.WorksProperties;
import com.gxar.quick.ar.server.base.common.utils.FileUtils;
import com.gxar.quick.ar.server.base.common.utils.ShareCodeGenerator;
import com.gxar.quick.ar.server.base.facade.ArProtocolConvertFacade;
import com.gxar.quick.ar.server.base.facade.ShareFacade;
import com.gxar.quick.ar.server.base.model.ResultCodeEnum;
import com.gxar.quick.ar.server.base.model.exchange.mapper.ExchangeItemMapstruct;
import com.gxar.quick.ar.server.base.model.exchange.vo.request.AdminExchangeItemTemplateShelvesRequest;
import com.gxar.quick.ar.server.base.model.exchange.vo.response.AdminExchangeItemTemplatePageResponse;
import com.gxar.quick.ar.server.base.model.share.vo.request.UnlimitedQRCodeRequest;
import com.gxar.quick.ar.server.base.model.share.vo.response.UnlimitedQRCodeResponse;
import com.gxar.quick.ar.server.base.model.user.vo.response.UserInfoResponse;
import com.gxar.quick.ar.server.base.model.user.vo.response.UserRightsResponse;
import com.gxar.quick.ar.server.base.model.works.dto.UploadIdentifyDTO;
import com.gxar.quick.ar.server.base.model.works.dto.WorksContentDTO;
import com.gxar.quick.ar.server.base.model.works.dto.WorksListDTO;
import com.gxar.quick.ar.server.base.model.works.dto.WorksUploadIdentifyDTO;
import com.gxar.quick.ar.server.base.model.works.mapper.WorksDraftMapStruct;
import com.gxar.quick.ar.server.base.model.works.mapper.WorksMapStruct;
import com.gxar.quick.ar.server.base.model.works.vo.BaseWorksContentVO;
import com.gxar.quick.ar.server.base.model.works.vo.BaseWorksVO;
import com.gxar.quick.ar.server.base.model.works.vo.request.*;
import com.gxar.quick.ar.server.base.model.works.vo.response.*;
import com.gxar.quick.ar.server.base.reference.UaaReference;
import com.gxar.quick.ar.server.base.sdk.IdentifySDK;
import com.gxar.quick.ar.server.db.mapper.*;
import com.gxar.quick.ar.server.db.model.*;
import com.gxar.quick.ar.server.db.repository.*;
import com.gxar.uaa.api.dto.user.SysUserId;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @author linsy
 * @version 1.0
 * @created 2023/11/7 15:36
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WorksService {

    private final WorksProperties worksProperties;
    private final IdentifyProperties identifyProperties;

    private final WorksDraftRepository worksDraftRepository;
    private final WorksRepository worksRepository;
    private final WorksTemplateRepository worksTemplateRepository;
    private final UserService userService;
    private final RedisService redisService;
    private final WorksMapper worksMapper;
    private final UserWorksMapper userWorksMapper;
    private final WorksDraftMapper worksDraftMapper;
    private final SlotRepository slotRepository;
    private final SlotService slotService;
    private final WorksShareRepository worksShareRepository;
    private final WorksImpressionsLogMapper worksImpressionsLogMapper;
    private final UserWorksRepository userWorksRepository;
    private final SlotMapper slotMapper;
    private final PlazaMapper plazaMapper;
    private final UserConfigMapper userConfigMapper;
    private final WorksTransferMapper worksTransferMapper;
    private final UserInfoMapper userInfoMapper;
    private final WorksTransferRepository worksTransferRepository;
    private final ExchangeItemUserMapper exchangeItemUserMapper;

    private final ThreadPoolTaskExecutor asyncExecutor;
    private final ShareFacade shareFacade;

    private final IdentifySDK identifySdk;

    private final ArProtocolConvertFacade arProtocolConvertFacade;

    /**
     * 创建作品草稿
     *
     * @param userProfile             用户信息
     * @param worksDraftCreateRequest 请求
     * @return 响应
     */
    @Transactional(rollbackFor = Throwable.class)
    public WorksDraftCreateResponse createDraft(UserProfile userProfile, WorksDraftCreateRequest worksDraftCreateRequest) {
        // 先校验用户
        userService.userInfo(userProfile);
        WorksDraftEntity oriWorkDraftEntity = new WorksDraftEntity();
        if (StringUtils.isNotBlank(worksDraftCreateRequest.getId())) {
            Optional<WorksDraftEntity> worksDraftEntityOptional = worksDraftRepository.findById(Long.valueOf(worksDraftCreateRequest.getId()));
            if (worksDraftEntityOptional.isPresent()) {
                oriWorkDraftEntity = worksDraftEntityOptional.get();
            }
        }
        // 多线程处理上传文件的大小
        this.fileSize(worksDraftCreateRequest, null, null);
        // 校验文件大小
        Map<Integer, ComboConfigItemEnum> itemMap = new HashMap<>(4);
        itemMap.put(ComboConfigItemEnum.IDENTIFY.getCode(), ComboConfigItemEnum.IDENTIFY);
        itemMap.put(ComboConfigItemEnum.VIDEO.getCode(), ComboConfigItemEnum.VIDEO);
        itemMap.put(ComboConfigItemEnum.UNITY3D_MODEL.getCode(), ComboConfigItemEnum.UNITY3D_MODEL);
        UserRightsResponse userRightsResponse = userService.userRights(userProfile, itemMap);
        this.verifySize(worksDraftCreateRequest, oriWorkDraftEntity.getIdentifySize(), oriWorkDraftEntity.getVideoSize(), oriWorkDraftEntity.getU3dModelSize(), userRightsResponse);

        WorksDraftEntity entity = WorksDraftMapStruct.INSTANCE.createToEntity(worksDraftCreateRequest);
        Date today = new Date();
        entity.setCreatedAt(today);
        entity.setUserId(userProfile.getId());
        entity.setCreator(userProfile.getMobile());
        entity.setUpdatedAt(today);
        entity.setOperator(userProfile.getMobile());
        entity.setDeleted(DeletedEnum.FALSE.getCode());
        // 以下字段不支持修改
        if (Objects.nonNull(oriWorkDraftEntity.getId())) {
            entity.setId(oriWorkDraftEntity.getId());
            entity.setProductionMethod(oriWorkDraftEntity.getProductionMethod());
            entity.setEditor(oriWorkDraftEntity.getEditor());
            entity.setCreatedAt(oriWorkDraftEntity.getCreatedAt());
            entity.setUserId(oriWorkDraftEntity.getUserId());
            entity.setCreator(oriWorkDraftEntity.getCreator());
        }
        if (StringUtils.isNotBlank(worksDraftCreateRequest.getWorksId())) {
            WorksEntity worksEntity = this.queryWorks(userProfile, Long.valueOf(worksDraftCreateRequest.getWorksId()));
            if (Objects.isNull(worksEntity)) {
                throw new APIRuntimeException(ResultCodeEnum.WORK_NOT_EXIST);
            }
        }
        if (StringUtils.isNotBlank(worksDraftCreateRequest.getTemplateId())) {
            WorksTemplateEntity worksTemplateEntity = this.queryWorksTemplate(userProfile, Long.valueOf(worksDraftCreateRequest.getTemplateId()));
            if (Objects.isNull(worksTemplateEntity)) {
                throw new APIRuntimeException(ResultCodeEnum.WORK_TEMPLATE_NOT_EXIST);
            }
        }
        // 校验草稿数量
        Integer limit = worksProperties.getDraftProperties().getLimit();
        Integer countByUser = worksDraftMapper.countByUser(userProfile.getId());
        if (countByUser >= limit) {
            throw new APIRuntimeException(ResultCodeEnum.WORK_DRAFTS_LIMIT);
        }
        WorksDraftEntity worksDraftEntity = worksDraftRepository.save(entity);
        return WorksDraftMapStruct.INSTANCE.entityToCreateResponse(worksDraftEntity);
    }

    /**
     * 作品列表
     *
     * @param userProfile  用户信息
     * @param nextId       下一个查询id
     * @param type         作品类型
     * @param source       来源
     * @param queryChannel 查询渠道
     * @return 响应
     */
    public SlippageResult<WorksListResponse> list(UserProfile userProfile, String nextId, UserWorksTypeEnum type, List<Integer> source, Integer queryChannel) {
        int pageNumber = 1;
        if (StringUtils.isNotBlank(nextId)) {
            String pageKey = String.format(RedisConstant.WORKS_PAGE_KEY, type.getName(), userProfile.getId(), nextId);
            Object pageInfo = redisService.get(pageKey);
            if (pageInfo != null) {
                pageNumber = Integer.parseInt(pageInfo.toString());
                redisService.remove(pageKey);
            }
        }
        PageHelper.startPage(pageNumber, WorksConstant.DEFAULT_QUERY_LIMIT);
        List<WorksListDTO> worksListDtoList;
        if (UserWorksTypeEnum.EXCHANGE.equals(type)) {
            worksListDtoList = exchangeItemUserMapper.queryWorks(userProfile.getId());
        } else {
            worksListDtoList = worksMapper.queryPage(userProfile.getId(), type.getCode(), source, queryChannel);
        }
        if (CollectionUtils.isEmpty(worksListDtoList)) {
            return SlippageResult.build(null, Collections.emptyList());
        }
        // 查询作品作者
        List<Long> userIdList = worksListDtoList.stream().map(WorksListDTO::getUserId).distinct().collect(Collectors.toList());
        List<UserConfigEntity> userConfigEntityList = userConfigMapper.findAllByUserId(userIdList);
        Map<Long, List<UserConfigEntity>> userConfigListMap = Optional.ofNullable(userConfigEntityList).orElse(Collections.emptyList()).stream().collect(Collectors.groupingBy(UserConfigEntity::getUserId));

        List<WorksListResponse> response = WorksMapStruct.INSTANCE.listDtoToListResponses(worksListDtoList);
        for (WorksListResponse listResponse : response) {
            if (type.equals(UserWorksTypeEnum.FAVORITES)) {
                // 收藏作品，年-月-日
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
                String dateFormat = simpleDateFormat.format(listResponse.getCreatedAt());
                listResponse.setTag(dateFormat);
            } else {
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM-dd");
                String dateFormat = simpleDateFormat.format(listResponse.getCreatedAt());
                String numberFormat = String.format("%02d", response.indexOf(listResponse) + 1);
                listResponse.setTag(dateFormat + "-" + numberFormat);
            }
            List<UserConfigEntity> userConfigEntities = userConfigListMap.get(listResponse.getUserId());
            if (CollectionUtils.isNotEmpty(userConfigEntities)) {
                UserConfigEntity userConfigEntity = userConfigEntities.stream().filter(e -> e.getItem().equals(ComboConfigItemEnum.WATERMARK.getCode())).findFirst().orElse(new UserConfigEntity());
                if (userConfigEntity.getAvailable() != null) {
                    listResponse.setWatermark(userConfigEntity.getAvailable());
                } else {
                    listResponse.setWatermark(1);
                }
            }
        }
        List<WorksListResponse> listResponseList = response.stream().sorted(Comparator.comparing(WorksListResponse::getStatus).reversed()).collect(Collectors.toList());
        String nextedId = SnowFlakeUtils.getInstance().nextId();
        String pageKey = String.format(RedisConstant.WORKS_PAGE_KEY, type.getName(), userProfile.getId(), nextedId);
        redisService.set(pageKey, pageNumber + 1);
        redisService.expire(pageKey, RedisConstant.DEFAULT_EXPIRE_SECONDS);
        return SlippageResult.build(nextedId, listResponseList);
    }

    /**
     * 查看作品详情
     *
     * @param userProfile 用户id
     * @param id          作品id
     * @return 作品信息
     */
    public Result<WorksInfoResponse> getDetail(UserProfile userProfile, Long id) {
        Optional<WorksEntity> worksEntityOptional = worksRepository.findById(id);
        if (worksEntityOptional.isPresent()) {
            WorksEntity worksEntity = worksEntityOptional.get();
            if (worksEntity.getUserId().equals(userProfile.getId())
                    && DeletedEnum.FALSE.getCode().equals(worksEntity.getDeleted())) {
                WorksInfoResponse worksInfoResponse = WorksMapStruct.INSTANCE.entityToInfoResponse(worksEntity);
                List<Integer> userWorkSource = Arrays.asList(UserWorksSourceEnum.PLAZA.getCode(), UserWorksSourceEnum.SELF.getCode(), UserWorksSourceEnum.SHARE.getCode());
                List<UserWorksEntity> userWorksEntity = userWorksMapper.queryByWorksAndSources(userProfile.getId(), id, UserWorksTypeEnum.FAVORITES.getCode(), userWorkSource);
                if (CollectionUtils.isNotEmpty(userWorksEntity)) {
                    worksInfoResponse.setFavorites(1);
                } else {
                    worksInfoResponse.setFavorites(0);
                }
                return Result.success(worksInfoResponse);
            }
        }
        return Result.error(ResultCodeEnum.WORK_NOT_EXIST);
    }

    /**
     * 更新作品
     *
     * @param userProfile        用户信息
     * @param id                 作品id
     * @param worksUpdateRequest 更新作品请求
     * @return 响应
     */
    @Transactional(rollbackFor = Throwable.class)
    public Result<WorksUpdateResponse> updateWorks(UserProfile userProfile, Long id, WorksUpdateRequest worksUpdateRequest) {
        Optional<WorksEntity> worksEntityOptional = worksRepository.findById(id);
        if (worksEntityOptional.isPresent()) {
            WorksEntity worksEntity = worksEntityOptional.get();
            if (!worksEntity.getUserId().equals(userProfile.getId())
                    || DeletedEnum.TRUE.getCode().equals(worksEntity.getDeleted())) {
                throw new APIRuntimeException(ResultCodeEnum.WORK_NOT_EXIST);
            }
            worksEntity.setName(worksUpdateRequest.getName());
            worksEntity.setCoverPage(worksUpdateRequest.getCoverPage());
            worksEntity.setIdentify(worksUpdateRequest.getIdentify());
            worksEntity.setContent(worksUpdateRequest.getContent());
            this.fileSize(worksUpdateRequest, worksEntity.getIdentify(), worksEntity.getIdentifySize());
            worksEntity.setIdentifySize(worksUpdateRequest.getIdentifySize().intValue());
            worksEntity.setVideoSize(worksUpdateRequest.getVideoSize().intValue());
            worksEntity.setU3dModelSize(worksUpdateRequest.getU3dModelSize().intValue());
            worksEntity.setUpdatedAt(new Date());
            worksEntity.setOperator(userProfile.getMobile());
            WorksEntity saved = worksRepository.save(worksEntity);
            WorksUpdateResponse worksUpdateResponse = WorksMapStruct.INSTANCE.entityToUpdateResponse(saved);
            return Result.success(worksUpdateResponse);
        }
        return Result.error(ResultCodeEnum.WORK_NOT_EXIST);
    }

    /**
     * 客户端更新作品位置
     *
     * @param userProfile 用户信息
     * @param request     更新请求
     * @return 响应
     */
    @Transactional(rollbackFor = Throwable.class)
    public Result<WorksUpdateResponse> updateWorksPositionByClient(UserProfile userProfile, WorksUpdatePositionRequest request) {
        Optional<WorksEntity> worksEntityOptional = worksRepository.findById(request.getWorkId());
        if (worksEntityOptional.isPresent()) {
            WorksEntity worksEntity = worksEntityOptional.get();
            if (!worksEntity.getUserId().equals(userProfile.getId()) || DeletedEnum.TRUE.getCode().equals(worksEntity.getDeleted())) {
                throw new APIRuntimeException(ResultCodeEnum.WORK_NOT_EXIST);
            }

            String newContent = arProtocolConvertFacade.updateWorkPositionByClient(request, worksEntity);
            worksEntity.setContent(newContent);
            worksEntity.setUpdatedAt(new Date());
            worksEntity.setOperator(userProfile.getMobile());
            WorksEntity saved = worksRepository.save(worksEntity);
            WorksUpdateResponse worksUpdateResponse = WorksMapStruct.INSTANCE.entityToUpdateResponse(saved);
            return Result.success(worksUpdateResponse);
        }
        return Result.error(ResultCodeEnum.WORK_NOT_EXIST);
    }

    /**
     * 删除作品
     *
     * @param userProfile 用户信息
     * @param id          作品id
     * @return 响应
     */
    @Transactional(rollbackFor = Throwable.class)
    public Result<Boolean> deleteWorks(UserProfile userProfile, Long id) {
        Optional<WorksEntity> worksEntityOptional = worksRepository.findById(id);
        if (worksEntityOptional.isPresent()) {
            WorksEntity worksEntity = worksEntityOptional.get();
            if (!worksEntity.getUserId().equals(userProfile.getId()) && DeletedEnum.TRUE.getCode().equals(worksEntity.getDeleted())) {
                throw new APIRuntimeException(ResultCodeEnum.WORK_NOT_EXIST);
            }
            worksEntity.setDeleted(DeletedEnum.TRUE.getCode());
            worksEntity.setUpdatedAt(new Date());
            List<UserWorksEntity> userWorksEntities = userWorksMapper.queryByWorkId(id, UserWorksTypeEnum.MINE.getCode());
            if (CollectionUtils.isNotEmpty(userWorksEntities)) {
                userWorksRepository.deleteAllByIdInBatch(userWorksEntities.stream().map(UserWorksEntity::getId).collect(Collectors.toList()));
            }
            slotMapper.batchDeleteByWorksId(Collections.singletonList(id), 1);
            worksRepository.save(worksEntity);
            // 删除该识别图
            String identifyUrl = this.getIdentifyUrl(worksEntity.getIdentify());
            if (StringUtils.isNotBlank(identifyUrl)) {
                try {
                    String errorMsg = identifySdk.delIdentify(userProfile.getId(), worksEntity.getId(), identifyUrl);
                    if (StringUtils.isNotBlank(errorMsg)) {
                        String notFound = "there are not math image";
                        if (!errorMsg.equals(notFound)) {
                            throw new APIRuntimeException(ResultCodeEnum.WORK_IDENTIFY_DEL_ERROR);
                        }
                        log.info("删除作品，删除识别图失败: [{}]。用户ID:[{}], 作品ID: [{}], 识别图地址: [{}]", errorMsg, userProfile.getId(), worksEntity.getId(), identifyUrl);
                    }
                } catch (Exception ex) {
                    log.info("批量删除作品，删除识别图异常: [{}]。用户ID:[{}], 作品ID: [{}], 识别图地址: [{}]", ex.getMessage(), userProfile.getId(), worksEntity.getId(), identifyUrl);
                    throw new APIRuntimeException(ResultCodeEnum.WORK_IDENTIFY_DEL_ERROR);
                }
            }
            if (WorksStatusEnum.ONLINE.getCode().equals(worksEntity.getStatus())) {
                List<WorksEntity> offlineWorks = worksMapper.queryUserOfflineOrderByCreatedAt(userProfile.getId(), 1);
                if (CollectionUtils.isNotEmpty(offlineWorks)) {
                    worksMapper.batchOnline(offlineWorks.stream().map(WorksEntity::getId).collect(Collectors.toList()), offlineWorks.size());
                    List<Long> slotIdList = offlineWorks.stream().map(WorksEntity::getSlotId).distinct().collect(Collectors.toList());
                    UserInfoResponse userInfoResponse = userService.userInfo(userProfile);
                    slotMapper.batchAvailable(slotIdList, slotIdList.size(), userInfoResponse.getComboEndTime());
                }
            }
        }
        return Result.success(true);
    }

    /**
     * 批量删除作品
     *
     * @param userProfile 用户配置
     * @param ids         作品id集合
     * @return 响应
     */
    @Transactional(rollbackFor = Throwable.class)
    public Result<Boolean> batchDeleteWorks(UserProfile userProfile, List<Long> ids) {
        List<WorksEntity> worksEntities = worksRepository.findAllById(ids);
        if (CollectionUtils.isEmpty(worksEntities)) {
            throw new APIRuntimeException(ResultCodeEnum.DELETE_WORK_NOT_EXIST);
        }
        List<WorksEntity> worksEntityList = worksEntities.stream().filter(e -> e.getUserId().equals(userProfile.getId())).collect(Collectors.toList());
        if (ids.size() != worksEntityList.size()) {
            throw new APIRuntimeException(ResultCodeEnum.DELETE_WORK_NOT_SELF);
        }
        worksMapper.batchDelete(ids, ids.size());
        userWorksMapper.deleteByTypeWorks(ids, UserWorksTypeEnum.MINE.getCode());
        slotMapper.batchDeleteByWorksId(ids, ids.size());
        // 批量删除识别图
        worksEntityList.forEach(e -> {
            String identifyUrl = this.getIdentifyUrl(e.getIdentify());
            if (StringUtils.isNotBlank(identifyUrl)) {
                try {
                    String errorMsg = identifySdk.delIdentify(userProfile.getId(), e.getId(), identifyUrl);
                    if (StringUtils.isNotBlank(errorMsg)) {
                        String notFound = "there are not math image";
                        if (!errorMsg.equals(notFound)) {
                            throw new APIRuntimeException(ResultCodeEnum.WORK_IDENTIFY_DEL_ERROR);
                        }
                        log.info("批量删除作品，删除识别图失败: [{}]。用户ID:[{}], 作品ID: [{}], 识别图地址: [{}]", errorMsg, userProfile.getId(), e.getId(), identifyUrl);
                    }
                } catch (Exception ex) {
                    log.info("批量删除作品，删除识别图异常: [{}]。用户ID:[{}], 作品ID: [{}], 识别图地址: [{}]", ex.getMessage(), userProfile.getId(), e.getId(), identifyUrl);
                }
            }
        });
        // 删除的在线的几个将补几个
        long onlineAmount = worksEntities.stream().filter(e -> WorksStatusEnum.ONLINE.getCode().equals(e.getStatus())).count();
        if (onlineAmount > 0) {
            List<WorksEntity> offlineWorks = worksMapper.queryUserOfflineOrderByCreatedAt(userProfile.getId(), (int) onlineAmount);
            if (CollectionUtils.isNotEmpty(offlineWorks)) {
                worksMapper.batchOnline(offlineWorks.stream().map(WorksEntity::getId).collect(Collectors.toList()), offlineWorks.size());
                List<Long> slotIdList = offlineWorks.stream().map(WorksEntity::getSlotId).distinct().collect(Collectors.toList());
                UserInfoResponse userInfoResponse = userService.userInfo(userProfile);
                slotMapper.batchAvailable(slotIdList, slotIdList.size(), userInfoResponse.getComboEndTime());
            }
        }
        return Result.success(true);
    }

    /**
     * 作品发布
     *
     * @param userProfile         用户信息
     * @param worksPublishRequest 作品发布请求
     * @return 响应
     */
    @Transactional(rollbackFor = Throwable.class)
    public WorksPublishResponse publish(UserProfile userProfile, WorksPublishRequest worksPublishRequest) {
        // 先校验用户
        userService.userInfo(userProfile);
        Long worksId = Long.valueOf(SnowFlakeUtils.getInstance().nextId());
        // 原作品的坑位
        Long slotId = null;
        WorksEntity worksEntity = new WorksEntity();
        JSONObject newIdentifyObject = JSONObject.parseObject(worksPublishRequest.getIdentify());
        String newIdentifyUrl = "";
        if (newIdentifyObject != null) {
            newIdentifyUrl = newIdentifyObject.getString("linkUrl");
        }
        // 二次发布作品
        if (StringUtils.isNotBlank(worksPublishRequest.getWorksId())) {
            Optional<WorksEntity> worksEntityOptional = worksRepository.findById(Long.valueOf(worksPublishRequest.getWorksId()));
            if (worksEntityOptional.isPresent()) {
                worksEntity = worksEntityOptional.get();
                worksId = worksEntity.getId();
                slotId = worksEntity.getSlotId();
                // 校验作品是否已经下线，已下线无法发布
                if (WorksStatusEnum.OFFLINE.getCode().equals(worksEntityOptional.get().getStatus())) {
                    throw new APIRuntimeException(ResultCodeEnum.WORK_OFFLINE_NOT_PUBLISH);
                }
            } else {
                throw new APIRuntimeException(ResultCodeEnum.WORK_NOT_EXIST);
            }
        }
        Integer oriIdentifySize = worksEntity.getIdentifySize();
        Integer oriVideoSize = worksEntity.getVideoSize();
        Integer oriU3dModelSize = worksEntity.getU3dModelSize();
        // 将草稿作品发布成作品
        Long workDraftId = worksPublishRequest.getWorkDraftId();
        if (Objects.nonNull(workDraftId)) {
            Optional<WorksDraftEntity> worksDraftEntityOptional = worksDraftRepository.findById(workDraftId);
            if (worksDraftEntityOptional.isPresent()) {
                WorksDraftEntity workDraft = worksDraftEntityOptional.get();
                if (DeletedEnum.TRUE.getCode().equals(workDraft.getDeleted())) {
                    throw new APIRuntimeException(ResultCodeEnum.WORK_DRAFTS_NOT_EXIST);
                }
                workDraft.setDeleted(DeletedEnum.TRUE.getCode());
                workDraft.setUpdatedAt(new Date());
                workDraft.setOperator(userProfile.getMobile());
                worksDraftRepository.save(workDraft);
                // 将草稿发布成作品，需要扣除草稿的文件大小
                oriIdentifySize = oriIdentifySize == null ? workDraft.getIdentifySize() : Math.addExact(oriIdentifySize, workDraft.getIdentifySize());
                oriVideoSize = oriVideoSize == null ? workDraft.getVideoSize() : Math.addExact(oriVideoSize, workDraft.getVideoSize());
                oriU3dModelSize = oriU3dModelSize == null ? workDraft.getU3dModelSize() : Math.addExact(oriU3dModelSize, workDraft.getU3dModelSize());
            }
        }
        // 多线程处理上传文件的大小
        this.fileSize(worksPublishRequest, worksEntity.getIdentify(), worksEntity.getIdentifySize());
        // 校验权益
        Map<Integer, ComboConfigItemEnum> itemMap = new HashMap<>(4);
        itemMap.put(ComboConfigItemEnum.SLOT.getCode(), ComboConfigItemEnum.SLOT);
        itemMap.put(ComboConfigItemEnum.IDENTIFY.getCode(), ComboConfigItemEnum.IDENTIFY);
        itemMap.put(ComboConfigItemEnum.VIDEO.getCode(), ComboConfigItemEnum.VIDEO);
        itemMap.put(ComboConfigItemEnum.UNITY3D_MODEL.getCode(), ComboConfigItemEnum.UNITY3D_MODEL);
        UserRightsResponse userRightsResponse = userService.userRights(userProfile, itemMap);
        // 坑位权益
        // 如果坑位为空说明用的是新坑位，需要校验是否超出套餐可用坑位
        if (Objects.isNull(slotId) && userRightsResponse.getAvailableSlotNumber() <= 0) {
            throw new APIRuntimeException(ResultCodeEnum.NO_SLOT_AVAILABLE);
        }
        this.verifySize(worksPublishRequest, oriIdentifySize, oriVideoSize, oriU3dModelSize, userRightsResponse);

        Date date = new Date();
        SlotEntity slotEntity = slotService.buildSlot(slotId, userProfile, date, worksId);

        // 创建作品
        WorksEntity newWorksEntity = buildWorksEntity(worksId, worksEntity, userProfile, date, slotEntity.getId(), worksPublishRequest);
        worksRepository.save(newWorksEntity);
        // 创建坑位
        slotRepository.save(slotEntity);
        // 创建我自制作品
        UserWorksEntity userWorks = userWorksMapper.queryByWorksAndTypeAndSource(worksId, userProfile.getId(), UserWorksSourceEnum.SELF.getCode(), UserWorksTypeEnum.MINE.getCode());
        if (Objects.isNull(userWorks)) {
            UserWorksEntity userWorksEntity = buildUserworksEntity(null, worksId, userProfile, UserWorksTypeEnum.MINE, UserWorksSourceEnum.SELF, date);
            userWorksRepository.save(userWorksEntity);
        }
        // 不上传云识别的用户ID
        List<Long> notCloudIdentifyUserId = worksProperties.getIdentifyProperties().getNotCloudIdentifyUserId();
        // 上传识别图
        if (StringUtils.isBlank(worksPublishRequest.getWorksId()) && !notCloudIdentifyUserId.contains(userProfile.getId())) {
            // 上传当前作品识别图
            String result = identifySdk.addIdentify(userProfile.getId(), newWorksEntity.getId(), newIdentifyUrl);
            if (StringUtils.isNotBlank(result)) {
                log.info("当前作品上传识别图失败：用户ID:[{}], 作品ID: [{}], 识别图地址: [{}], 错误原因：[{}]", userProfile.getId(), newWorksEntity.getId(), newIdentifyUrl, result);
                if (IdentifyConstant.IDENTIFY_SO_LIKE.equals(result)) {
                    throw new APIRuntimeException(ResultCodeEnum.WORK_IDENTIFY_LIKE_ERROR);
                }
            }
        }
        return WorksMapStruct.INSTANCE.entityToPublish(newWorksEntity);
    }

    /**
     * 回滚上传的识别图
     *
     * @param worksUploadIdentifyResult 上传结果
     * @param userProfile               用户信息
     */
    private void rollbackUploadIdentify(List<WorksUploadIdentifyDTO> worksUploadIdentifyResult, UserProfile userProfile) {
        if (CollectionUtils.isEmpty(worksUploadIdentifyResult)) {
            return;
        }
        worksUploadIdentifyResult.stream().filter(WorksUploadIdentifyDTO::getSuccess).forEach(e -> {
            try {
                String errorMsg = identifySdk.delIdentify(userProfile.getId(), e.getId(), e.getLinkUrl());
                if (StringUtils.isNotBlank(errorMsg)) {
                    log.error("批量删除历史作品，删除识别图失败: [{}]。用户ID:[{}], 作品ID: [{}], 识别图地址: [{}]", errorMsg, userProfile.getId(), e.getId(), e.getLinkUrl());
                }
            } catch (Exception ex) {
                log.error("批量删除历史作品，删除识别图异常: [{}]。用户ID:[{}], 作品ID: [{}], 识别图地址: [{}]", ex.getMessage(), userProfile.getId(), e.getId(), e.getLinkUrl());
            }
        });
    }

    /**
     * 批量上传识别图
     *
     * @param worksEntities 作品集合
     * @return 上传结果
     */
    public List<WorksUploadIdentifyDTO> batchUploadIdentify(List<UploadIdentifyDTO> worksEntities) {
        // 使用CompletableFuture处理集合中的元素
        List<CompletableFuture<WorksUploadIdentifyDTO>> futures = worksEntities.stream()
                .map(task -> CompletableFuture.supplyAsync(() -> {
                    try {
                        return uploadIdentify(task);
                    } catch (Exception e) {
                        log.error("批量上传识别图异常：作品id[{}]：{}", task.getId(), e.getMessage(), e);
                        throw new APIRuntimeException(ResultCodeEnum.WORK_IDENTIFY_UPLOAD_ERROR);
                    }
                }, asyncExecutor))
                .collect(Collectors.toList());

        // 等待所有任务完成
        CompletableFuture<Void> allOf = CompletableFuture.allOf(
                futures.toArray(new CompletableFuture[0])
        );

        // 等待所有任务完成
        try {
            allOf.get();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 获取处理结果
        return futures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList());
    }

    /**
     * 上传识别图
     *
     * @param entity 作品
     * @return 上传结果
     */
    private WorksUploadIdentifyDTO uploadIdentify(UploadIdentifyDTO entity) {
        JSONObject identifyObject = JSONObject.parseObject(entity.getIdentify());
        String linkUrl = identifyObject.getString("linkUrl");
        String result = identifySdk.addIdentify(entity.getUserId(), entity.getId(), linkUrl);
        WorksUploadIdentifyDTO worksContentDto = new WorksUploadIdentifyDTO();
        worksContentDto.setId(entity.getId());
        worksContentDto.setLinkUrl(linkUrl);
        if (StringUtils.isBlank(result)) {
            worksContentDto.setSuccess(true);
        } else {
            worksContentDto.setMsg(result);
            worksContentDto.setSuccess(false);
        }
        return worksContentDto;
    }

    /**
     * 校验文件大小
     *
     * @param request            新文件大小
     * @param oriIdentifySize    旧识别图大小
     * @param oriVideoSize       旧视频大小
     * @param oriU3dSize         旧3d模型大小
     * @param userRightsResponse 用户权益
     * @param <T>                新文件大小
     */
    private <T extends BaseWorksVO> void verifySize(T request, Integer oriIdentifySize, Integer oriVideoSize, Integer oriU3dSize, UserRightsResponse userRightsResponse) {
        // 校验识别图的大小是否足够
        Integer availableIdentifyNumber = userRightsResponse.getAvailableIdentifyNumber();
        // 如果是更新，剩余可用加上该作品的大小，后面再扣减
        if (oriIdentifySize != null) {
            availableIdentifyNumber = Math.addExact(oriIdentifySize, availableIdentifyNumber);
        }
        long identifySize = Math.subtractExact(availableIdentifyNumber, request.getIdentifySize());
        if (identifySize <= 0) {
            throw new APIRuntimeException(ResultCodeEnum.NO_IDENTIFY_SIZE_AVAILABLE);
        }

        // 校验视频的大小是否足够
        Integer availableVideoNumber = userRightsResponse.getAvailableVideoNumber();
        if (oriVideoSize != null) {
            availableVideoNumber = Math.addExact(oriVideoSize, availableVideoNumber);
        }
        long videoSize = Math.subtractExact(availableVideoNumber, request.getVideoSize());
        if (videoSize <= 0) {
            throw new APIRuntimeException(ResultCodeEnum.NO_VIDEO_SIZE_AVAILABLE);
        }

        Integer availableU3dModelNumber = userRightsResponse.getAvailableU3dModelNumber();
        if (oriU3dSize != null) {
            availableU3dModelNumber = Math.addExact(oriU3dSize, availableU3dModelNumber);
        }
        // 校验3D模型的大小是否足够
        long u3dSize = Math.subtractExact(availableU3dModelNumber, request.getU3dModelSize());
        if (u3dSize <= 0) {
            throw new APIRuntimeException(ResultCodeEnum.NO_3D_MODEL_SIZE_AVAILABLE);
        }
    }

    /**
     * 作品分享
     *
     * @param userProfile 用户信息
     * @param id          作品id
     * @return 作品分享的二维码地址
     */
    public WorksShareResponse share(UserProfile userProfile, Long id) {
        Optional<WorksEntity> worksEntityOptional = worksRepository.findById(id);
        if (!worksEntityOptional.isPresent()) {
            throw new APIRuntimeException(ResultCodeEnum.WORK_NOT_EXIST);
        }
        WorksEntity worksEntity = worksEntityOptional.get();
        if (DeletedEnum.TRUE.getCode().equals(worksEntity.getDeleted())) {
            throw new APIRuntimeException(ResultCodeEnum.WORK_NOT_EXIST);
        }
        if (!worksEntity.getUserId().equals(userProfile.getId())) {
            throw new APIRuntimeException(ResultCodeEnum.SHARE_OWN_WORKS);
        }
        if (WorksStatusEnum.OFFLINE.getCode().equals(worksEntity.getStatus())) {
            throw new APIRuntimeException(ResultCodeEnum.WORK_OFFLINE_NOT_SHARE);
        }
        String salt = ShareCodeGenerator.generateRandomSalt();
        String generateCode = ShareCodeGenerator.generateCode(userProfile.getId(), worksEntity.getId(), salt.getBytes());
        WorksShareEntity worksShareEntity = new WorksShareEntity();
        worksShareEntity.setWorksId(id);
        worksShareEntity.setShareCode(generateCode);
        worksShareEntity.setSalt(salt);
        worksShareEntity.setCreatedAt(new Date());
        worksShareEntity.setCreator(userProfile.getMobile());
        worksShareRepository.save(worksShareEntity);
        WorksShareResponse response = new WorksShareResponse();
        response.setShareCode(generateCode);
        return response;
    }

    /**
     * 根据分享码查询作品信息
     *
     * @param userProfile 用户信息
     * @param id          分享码
     * @return 分享作品信息
     */
    @Transactional(rollbackFor = Throwable.class)
    public Result<WorksShareInfoResponse> getDetailByWorksId(UserProfile userProfile, String id) {
        if (StringUtils.isBlank(id)) {
            return Result.error(ResultCodeEnum.WORK_SHARE_INFO_ERROR);
        }
        Optional<WorksEntity> worksEntityOptional = worksRepository.findById(Long.valueOf(id));
        if (worksEntityOptional.isPresent()) {
            WorksEntity worksEntity = worksEntityOptional.get();
            if (worksEntity.getUserId().equals(userProfile.getId())) {
                return Result.success();
            }
            // 作品已下线
            if (WorksStatusEnum.OFFLINE.getCode().equals(worksEntity.getStatus())) {
                return Result.error(ResultCodeEnum.WORK_SHARE_INFO_OFFLINE);
            }
            // 作品已删除
            if (DeletedEnum.TRUE.getCode().equals(worksEntity.getDeleted())) {
                return Result.error(ResultCodeEnum.WORK_SHARE_INFO_INVALID);
            }
            // 作品识别次数已上限
            UserProfile worksUser = new UserProfile();
            worksUser.setId(worksEntity.getUserId());
            UserRightsResponse rightsResponse = userService.userRights(worksUser, ComboConfigItemEnum.IMPRESSIONS.getItem());
            if (Objects.isNull(rightsResponse) || (rightsResponse.getAvailableImpressionsNumber() <= 0 && rightsResponse.getTotalImpressionsNumber() > 0)) {
                return Result.error(ResultCodeEnum.WORKS_IMPRESSIONS_LIMIT);
            }

            // 保存用户分享记录
            UserWorksEntity userWorks = userWorksMapper.queryByWorksAndTypeAndSource(null, userProfile.getId(), UserWorksSourceEnum.SHARE.getCode(), UserWorksTypeEnum.MINE.getCode());
            Long userWorksId = null;
            if (Objects.nonNull(userWorks)) {
                userWorksId = userWorks.getId();
            }
            UserWorksEntity userWorksEntity = buildUserworksEntity(userWorksId, worksEntity.getId(), userProfile, UserWorksTypeEnum.MINE, UserWorksSourceEnum.SHARE, new Date());
            userWorksRepository.save(userWorksEntity);
            // 上传当前作品识别图
            String result = identifySdk.addIdentify(userProfile.getId(), worksEntity.getId(), getIdentifyUrl(worksEntity.getIdentify()));
            if (StringUtils.isNotBlank(result)) {
                if (IdentifyConstant.IDENTIFY_SO_LIKE.equals(result)) {
                    throw new APIRuntimeException(ResultCodeEnum.WORK_IDENTIFY_LIKE_ERROR);
                }
            }
            WorksShareInfoResponse worksShareInfoResponse = WorksMapStruct.INSTANCE.entityToShareInfoResponse(worksEntity);
            return Result.success(worksShareInfoResponse);

        }
        return Result.error(ResultCodeEnum.WORK_SHARE_INFO_INVALID);
    }


    /**
     * 查询作品
     *
     * @param userProfile 用户信息
     * @param worksId     作品id
     * @return 响应作品
     */
    private WorksEntity queryWorks(UserProfile userProfile, Long worksId) {
        Optional<WorksEntity> worksEntityOptional = worksRepository.findById(worksId);
        if (worksEntityOptional.isPresent()) {
            WorksEntity worksEntity = worksEntityOptional.get();
            if (worksEntity.getUserId().equals(userProfile.getId())) {
                return worksEntity;
            }
        }
        return null;
    }

    /**
     * 查询作品模板
     *
     * @param userProfile     用户信息
     * @param worksTemplateId 作品模板id
     * @return 作品模板
     */
    private WorksTemplateEntity queryWorksTemplate(UserProfile userProfile, Long worksTemplateId) {
        Optional<WorksTemplateEntity> worksTemplateEntityOptional = worksTemplateRepository.findById(worksTemplateId);
        return worksTemplateEntityOptional.orElse(null);
    }

    /**
     * 作品收藏
     *
     * @param userProfile 用户信息
     * @param id          作品id
     * @return 响应
     */
    public Result<WorksFavoritesResponse> favorites(UserProfile userProfile, Long id) {
        Optional<WorksEntity> worksEntityOptional = worksRepository.findById(id);
        UserWorksSourceEnum userWorksSourceEnum = UserWorksSourceEnum.SHARE;
        if (worksEntityOptional.isPresent()) {
            WorksEntity worksEntity = worksEntityOptional.get();
            // 收藏自己的作品
            if (worksEntity.getUserId().equals(userProfile.getId())) {
                userWorksSourceEnum = UserWorksSourceEnum.SELF;
            }
        }
        UserWorksEntity userWorksEntity = userWorksMapper.queryByWorksAndTypeAndSource(id, userProfile.getId(), userWorksSourceEnum.getCode(), UserWorksTypeEnum.FAVORITES.getCode());
        if (Objects.nonNull(userWorksEntity)) {
            throw new APIRuntimeException(ResultCodeEnum.WORK_ALREADY_FAVORITES);
        } else {
            Date date = new Date();
            userWorksMapper.insert(id, userProfile.getId(), UserWorksTypeEnum.FAVORITES.getCode(), userWorksSourceEnum.getCode(), date, userProfile.getMobile(), date);
            return Result.success();
        }
    }

    /**
     * 取消收藏
     *
     * @param userProfile 用户
     * @param worksId     作品
     * @return 成功
     */
    @Transactional(rollbackFor = Throwable.class)
    public Result<WorksFavoritesResponse> cancelFavorites(UserProfile userProfile, Long worksId) {
        Optional<WorksEntity> worksEntityOptional = worksRepository.findById(worksId);
        if (worksEntityOptional.isPresent()) {
            // 如果用户是从广场收藏的，取消收藏要做一个广场收藏数量的减少
            UserWorksEntity userWorksEntity = userWorksMapper.queryByWorksAndSource(userProfile.getId(), worksId, UserWorksSourceEnum.PLAZA.getCode());
            if (Objects.nonNull(userWorksEntity)) {
                plazaMapper.decreaseFavoritesByWorksId(userWorksEntity.getWorksId(), 1);
            }
            userWorksMapper.deleteUserWorksByType(userProfile.getId(), worksId, UserWorksTypeEnum.FAVORITES.getCode());
        }
        return Result.success();
    }


    /**
     * 构建作品实体
     *
     * @param worksId             作品id
     * @param oriWorkEntity       原实体
     * @param userProfile         用户信息
     * @param date                日期
     * @param slotId              坑位id
     * @param worksPublishRequest 发布作品请求
     * @return 作品实体
     */
    private WorksEntity buildWorksEntity(Long worksId, WorksEntity oriWorkEntity, UserProfile userProfile, Date date, Long slotId, WorksPublishRequest worksPublishRequest) {
        WorksEntity worksEntity = WorksMapStruct.INSTANCE.publishToEntity(worksPublishRequest);
        // 识别图不可修改
        worksEntity.setIdentify(worksPublishRequest.getIdentify());
        worksEntity.setIdentifySize(worksPublishRequest.getIdentifySize().intValue());
        worksEntity.setId(worksId);
        worksEntity.setUserId(userProfile.getId());
        worksEntity.setSlotId(slotId);
        worksEntity.setImpressions(Objects.nonNull(oriWorkEntity.getImpressions()) ? oriWorkEntity.getImpressions() : 0);
        worksEntity.setStatus(WorksStatusEnum.ONLINE.getCode());
        worksEntity.setShelves(Objects.nonNull(oriWorkEntity.getShelves()) ? oriWorkEntity.getShelves() : false);
        worksEntity.setUserExchangeItemId(Objects.nonNull(oriWorkEntity.getUserExchangeItemId()) ? oriWorkEntity.getUserExchangeItemId() : null);
        worksEntity.setDeleted(DeletedEnum.FALSE.getCode());
        worksEntity.setCreatedAt(Objects.nonNull(oriWorkEntity.getCreatedAt()) ? oriWorkEntity.getCreatedAt() : date);
        worksEntity.setCreator(StringUtils.isNotBlank(oriWorkEntity.getCreator()) ? oriWorkEntity.getCreator() : userProfile.getMobile());
        worksEntity.setUpdatedAt(date);
        worksEntity.setOperator(userProfile.getMobile());
        return worksEntity;
    }

    /**
     * 构建自制用户作品
     *
     * @param worksId     作品id
     * @param userProfile 用户信息
     * @param date        日期
     * @return 用户作品实体
     */
    public UserWorksEntity buildUserworksEntity(Long id, Long worksId, UserProfile userProfile, UserWorksTypeEnum userWorksTypeEnum, UserWorksSourceEnum userWorksSourceEnum, Date date) {
        UserWorksEntity userWorksEntity = new UserWorksEntity();
        userWorksEntity.setId(id);
        userWorksEntity.setWorksId(worksId);
        userWorksEntity.setUserId(userProfile.getId());
        userWorksEntity.setType(userWorksTypeEnum.getCode());
        userWorksEntity.setSource(userWorksSourceEnum.getCode());
        userWorksEntity.setCreatedAt(date);
        userWorksEntity.setCreator(userProfile.getMobile());
        userWorksEntity.setUpdatedAt(date);
        return userWorksEntity;
    }

    /**
     * 作品识别
     *
     * @param userProfile 用户
     * @param id          作品
     * @return 响应
     */
    @Transactional(rollbackFor = Throwable.class)
    public Result<WorksScanResponse> scan(UserProfile userProfile, Long id) {
        Optional<WorksEntity> worksEntityOptional = worksRepository.findById(id);
        if (worksEntityOptional.isPresent()) {
            WorksEntity worksEntity = worksEntityOptional.get();
            if (DeletedEnum.FALSE.getCode().equals(worksEntity.getDeleted())) {
                // 校验作品识别次数是否上限
                UserProfile worksUser = new UserProfile();
                worksUser.setId(worksEntity.getUserId());
                UserRightsResponse rightsResponse = userService.userRights(worksUser, ComboConfigItemEnum.IMPRESSIONS.getItem());
                WorksScanResponse worksScanResponse = new WorksScanResponse();
                if (Objects.isNull(rightsResponse) || (rightsResponse.getAvailableImpressionsNumber() <= 0 && rightsResponse.getTotalImpressionsNumber() > 0)) {
                    worksScanResponse.setAvailableImpressionsNumber(0);
                    return Result.success(worksScanResponse);
                }
                // 浏览量+1
                worksMapper.increaseImpressions(worksEntity.getId(), 1);
                worksImpressionsLogMapper.insert(worksEntity.getId(), worksEntity.getSlotId(), userProfile.getId(), new Date(), userProfile.getMobile());
                worksScanResponse.setAvailableImpressionsNumber(rightsResponse.getAvailableImpressionsNumber() - 1);
                // 如果总识别次数<0,表示可以无限次识别
                if (rightsResponse.getTotalImpressionsNumber() < 0) {
                    worksScanResponse.setAvailableImpressionsNumber(1);
                }
                return Result.success(worksScanResponse);
            }
        }
        return Result.success();
    }

    /**
     * 获取作品的所有url
     *
     * @param worksId 作品id
     * @return url集合
     */
    public Set<String> getWorksLinkUrl(String worksId) {
        Set<String> resultset = new HashSet<>();
        if (StringUtils.isNotBlank(worksId)) {
            Optional<WorksEntity> worksEntityOptional = worksRepository.findById(Long.parseLong(worksId));
            if (worksEntityOptional.isPresent()) {
                WorksEntity entity = worksEntityOptional.get();
                try {
                    JSONObject identifyObject = JSONObject.parseObject(entity.getIdentify());
                    if (identifyObject != null) {
                        resultset.add(identifyObject.getString("linkUrl"));
                    }
                } catch (Exception e) {
                    // 解析识别图异常
                }
                try {
                    List<WorksContentDTO> worksContentDTOList = JSON.parseArray(entity.getContent(), WorksContentDTO.class);
                    List<String> contentLinkUrlList = Optional.ofNullable(worksContentDTOList).orElse(Collections.emptyList()).stream().map(WorksContentDTO::getLinkUrl).collect(Collectors.toList());
                    if (CollectionUtils.isNotEmpty(contentLinkUrlList)) {
                        resultset.addAll(contentLinkUrlList);
                    }
                } catch (Exception e) {
                    // 解析内容异常
                }
            }
        }
        return resultset;
    }


    /**
     * 获取草稿的所有url
     *
     * @param worksDraftId 草稿id
     * @return 作品草稿的url
     */
    public Set<String> getWorksDraftLinkUrl(String worksDraftId) {
        Set<String> resultset = new HashSet<>();
        if (StringUtils.isNotBlank(worksDraftId)) {
            Optional<WorksDraftEntity> worksDraftEntityOptional = worksDraftRepository.findById(Long.parseLong(worksDraftId));
            if (worksDraftEntityOptional.isPresent()) {
                WorksDraftEntity entity = worksDraftEntityOptional.get();
                try {
                    JSONObject identifyObject = JSONObject.parseObject(entity.getIdentify());
                    if (identifyObject != null) {
                        resultset.add(identifyObject.getString("linkUrl"));
                    }
                } catch (Exception e) {
                    // 解析识别图异常
                }
                try {
                    List<WorksContentDTO> worksContentDTOList = JSON.parseArray(entity.getContent(), WorksContentDTO.class);
                    List<String> contentLinkUrlList = Optional.ofNullable(worksContentDTOList).orElse(Collections.emptyList()).stream().map(WorksContentDTO::getLinkUrl).collect(Collectors.toList());
                    if (CollectionUtils.isNotEmpty(contentLinkUrlList)) {
                        resultset.addAll(contentLinkUrlList);
                    }
                } catch (Exception e) {
                    // 解析内容异常
                }
            }
        }
        return resultset;
    }


    /**
     * 多线程校验文件大小
     *
     * @param entity          实体
     * @param oriIdentifySize 原识别图大小
     */
    public <T extends BaseWorksContentVO> void fileSize(T entity, String oriIdentify, Integer oriIdentifySize) {
        String content = entity.getContent();
        List<WorksContentDTO> worksContentDtoList = JSON.parseArray(content, WorksContentDTO.class);
        if (StringUtils.isBlank(oriIdentify)) {
            JSONObject identifyObject = JSONObject.parseObject(entity.getIdentify());
            if (identifyObject != null) {
                String identifyUrl = identifyObject.getString("linkUrl");
                if (StringUtils.isBlank(identifyUrl)) {
                    throw new APIRuntimeException(ResultCodeEnum.WORK_IDENTIFY_NOT_NULL);
                }
                WorksContentDTO worksContentDto = new WorksContentDTO();
                worksContentDto.setType(ComboConfigItemEnum.IDENTIFY.getItem());
                worksContentDto.setLinkUrl(identifyObject.getString("linkUrl"));
                if (CollectionUtils.isEmpty(worksContentDtoList)) {
                    worksContentDtoList = Collections.singletonList(worksContentDto);
                } else {
                    worksContentDtoList.add(worksContentDto);
                }
            }
        }
        // 只校验识别图、3d模型、视频
        List<String> fileSizeCheckType = Lists.newArrayList(ComboConfigItemEnum.IDENTIFY.getItem(), WorksShowsTypeEnum.GLTF.getName(), WorksShowsTypeEnum.VIDEO_BOX.getName());
        worksContentDtoList = worksContentDtoList.stream().filter(e -> fileSizeCheckType.contains(e.getType())).collect(Collectors.toList());
        // 使用CompletableFuture处理集合中的元素
        List<CompletableFuture<WorksContentDTO>> futures = worksContentDtoList.stream()
                .map(task -> CompletableFuture.supplyAsync(() -> {
                    try {
                        return getFileSize(task);
                    } catch (Exception e) {
                        log.error("作品容量校验异常，请尝试重新上传作品内容，文件：[{}]：{}", task.getLinkUrl(), e.getMessage(), e);
                        throw new RuntimeException(e);
                    }
                }, asyncExecutor))
                .collect(Collectors.toList());

        // 等待所有任务完成
        CompletableFuture<Void> allOf = CompletableFuture.allOf(
                futures.toArray(new CompletableFuture[0])
        );

        // 等待所有任务完成
        try {
            allOf.get();
        } catch (Exception e) {
            throw new APIRuntimeException(ResultCodeEnum.FILE_SIZE_ERROR);
        }

        // 获取处理结果
        List<WorksContentDTO> results = futures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList());
        Map<String, List<WorksContentDTO>> worksContentDtoMap = results.stream().collect(Collectors.groupingBy(WorksContentDTO::getType));
        if (StringUtils.isBlank(oriIdentify)) {
            List<WorksContentDTO> identifyList = worksContentDtoMap.get(ComboConfigItemEnum.IDENTIFY.getItem());
            Long identifySize = Optional.ofNullable(identifyList).orElse(Collections.emptyList()).stream().map(WorksContentDTO::getSize).reduce(Long::sum).orElse(0L);
            entity.setIdentifySize(identifySize);
        } else {
            entity.setIdentifySize(Long.valueOf(oriIdentifySize));
        }
        // 3d模型
        List<WorksContentDTO> glftList = worksContentDtoMap.get(WorksShowsTypeEnum.GLTF.getName());
        Long gltfSize = Optional.ofNullable(glftList).orElse(Collections.emptyList()).stream().map(WorksContentDTO::getSize).reduce(Long::sum).orElse(0L);
        entity.setU3dModelSize(gltfSize);
        // 视频
        List<WorksContentDTO> videoList = worksContentDtoMap.get(WorksShowsTypeEnum.VIDEO_BOX.getName());
        Long videoSize = Optional.ofNullable(videoList).orElse(Collections.emptyList()).stream().map(WorksContentDTO::getSize).reduce(Long::sum).orElse(0L);
        entity.setVideoSize(videoSize);
    }

    /**
     * 获取文件大小
     *
     * @param task 业务模型
     * @return 响应
     */
    private static WorksContentDTO getFileSize(WorksContentDTO task) {
        log.info("线程：{},执行", Thread.currentThread().getName());
        // 模拟任务处理
        Long fileSize = FileUtils.getFileSize(task.getLinkUrl());
        task.setSize(fileSize);
        return task;
    }

    /**
     * 作品分享删除
     *
     * @param userProfile 用户信息
     * @param worksId     作品id
     * @return 响应
     */
    @Transactional(rollbackFor = Throwable.class)
    public WorksShareDeleteResponse shareDelete(UserProfile userProfile, Long worksId) {
        UserWorksEntity userWorks = userWorksMapper.queryByWorksAndTypeAndSource(worksId, userProfile.getId(), UserWorksSourceEnum.SHARE.getCode(), UserWorksTypeEnum.MINE.getCode());
        if (Objects.nonNull(userWorks)) {
            userWorksMapper.deleteById(userWorks.getId());
            // 删除原来的识别图
            Optional<WorksEntity> optionalWorksEntity = worksRepository.findById(userWorks.getWorksId());
            if (optionalWorksEntity.isPresent()) {
                WorksEntity entity = optionalWorksEntity.get();
                String identifyUrl = getIdentifyUrl(entity.getIdentify());
                try {
                    String error = identifySdk.delIdentify(userProfile.getId(), userWorks.getWorksId(), identifyUrl);
                    if (StringUtils.isNotBlank(error)) {
                        String notFound = "there are not math image";
                        if (!error.equals(notFound)) {
                            throw new APIRuntimeException(ResultCodeEnum.ORI_SHARE_IDENTIFY_DEL_ERROR);
                        }
                        log.info("他人分享作品删除识别图异常：作品ID: [{}], 用户ID：[{}], 错误信息:[{}]", userWorks.getWorksId(), userProfile.getId(), error);
                    }
                } catch (Exception e) {
                    log.error("他人分享作品删除识别图异常：作品ID: [{}], 用户ID：[{}], 错误信息:[{}]", userWorks.getWorksId(), userProfile.getId(), e.getMessage(), e);
                    throw new APIRuntimeException(ResultCodeEnum.ORI_SHARE_IDENTIFY_DEL_ERROR);
                }
            }
        }
        return new WorksShareDeleteResponse();
    }

    /**
     * 作品转移
     *
     * @param userProfile 用户信息
     * @param request     请求
     * @return 响应
     */
    @Transactional(rollbackFor = Throwable.class)
    public WorkTransferResponse transferWorks(UserProfile userProfile, WorkTransferRequest request) {
        long batchId = Long.parseLong(SnowFlakeUtils.getInstance().nextId());
        WorkTransferMethodEnum workTransferMethodEnum = WorkTransferMethodEnum.getByCode(request.getWorkTransferMethod());
        if (Objects.isNull(workTransferMethodEnum)) {
            throw new APIRuntimeException(ResultCodeEnum.WORK_TRANSFER_METHOD_ERROR);
        }
        if (Objects.isNull(request.getPhone()) && Objects.isNull(request.getUserId())) {
            throw new APIRuntimeException(ResultCodeEnum.TRANSFER_USER_NOT_NULL);
        }
        WorkTransferResponse response = new WorkTransferResponse();
        List<WorksTransferEntity> workTransferEntityList = new ArrayList<>();
        UserProfile targetUser = new UserProfile();
        targetUser.setId(request.getUserId());
        targetUser.setMobile(request.getUserId().toString());

        if (Objects.isNull(request.getUserId())) {
            UserInfoEntity userInfoEntity = userInfoMapper.findByCreator(request.getPhone());
            if (Objects.nonNull(userInfoEntity)) {
                targetUser.setId(userInfoEntity.getUserId());
                targetUser.setMobile(request.getPhone());
                request.setUserId(userInfoEntity.getUserId());
            } else {
                targetUser.setId(worksProperties.getUserInfoProperties().getDefaultUserId());
                targetUser.setMobile(worksProperties.getUserInfoProperties().getDefaultPhone());
                request.setUserId(worksProperties.getUserInfoProperties().getDefaultUserId());
            }
        }
        List<Long> worksId = request.getWorksId();
        List<WorksEntity> worksEntityList = worksMapper.queryIdsAndUserId(worksId, userProfile.getId());
        if (CollectionUtils.isEmpty(worksEntityList) || worksEntityList.size() != worksId.size()) {
            throw new APIRuntimeException(ResultCodeEnum.WORK_NOT_EXIST);
        }
        List<SlotEntity> slotEntityList = new ArrayList<>();
        Date date = new Date();
        List<UserWorksEntity> userWorksEntities = userWorksMapper.queryByInWorksAndTypeAndSource(worksId, userProfile.getId(), UserWorksSourceEnum.SELF.getCode(), UserWorksTypeEnum.MINE.getCode());
        this.transferWorks(userProfile, targetUser, request.getSlot(), date, worksId, userProfile.getId(), worksEntityList, slotEntityList, userWorksEntities);
        if (!request.getSlot()) {
            slotMapper.batchDeleteByWorksId(worksId, worksId.size());
        }
        if (CollectionUtils.isNotEmpty(slotEntityList)) {
            slotMapper.saveAll(slotEntityList);
        }
        if (CollectionUtils.isNotEmpty(worksEntityList)) {
            worksRepository.saveAll(worksEntityList);
        }
        if (CollectionUtils.isNotEmpty(userWorksEntities)) {
            userWorksRepository.saveAll(userWorksEntities);
        }
        this.buildTransferEntity(workTransferEntityList, worksEntityList, batchId, request, workTransferMethodEnum, userProfile, date);
        worksTransferRepository.saveAll(workTransferEntityList);
        if (WorkTransferMethodEnum.MANUAL.equals(workTransferMethodEnum)) {
            // 生成领取作品太阳图
            UnlimitedQRCodeRequest unlimitedQRCodeRequest = new UnlimitedQRCodeRequest();
            unlimitedQRCodeRequest.setWorksId(String.valueOf(batchId));
            unlimitedQRCodeRequest.setPage(request.getPage());
            unlimitedQRCodeRequest.setEnv(request.getEnv());
            UnlimitedQRCodeResponse unlimitedQRCode = shareFacade.getUnlimitedQRCode(unlimitedQRCodeRequest);
            response.setBuffer(unlimitedQRCode.getBuffer());
        }
        return response;
    }

    /**
     * 作品转移领取
     *
     * @param userProfile 领取的用户
     * @param request     请求
     * @return 响应
     */
    @Transactional(rollbackFor = Throwable.class)
    public WorkTransferReceiveResponse transferReceive(UserProfile userProfile, WorkTransferReceiveRequest request) {
        Long batchId = request.getBatchId();
        List<WorksTransferEntity> worksTransferEntities = worksTransferMapper.queryByBatchId(batchId);
        if (CollectionUtils.isEmpty(worksTransferEntities)) {
            throw new APIRuntimeException(ResultCodeEnum.NOT_WORKS_CAN_RECEIVE);
        }
        // 赠送的目标用户与领取的用户不是同一个
        boolean userInconsistent = worksTransferEntities.stream().anyMatch(e -> !e.getTargetUserId().equals(userProfile.getId()));
        if (userInconsistent) {
            throw new APIRuntimeException(ResultCodeEnum.NOT_PERMISSIONS_WORKS_RECEIVE);
        }
        List<WorksTransferEntity> pending = worksTransferEntities.stream().filter(e -> WorkTransferStatusEnum.PENDING.getCode().equals(e.getStatus())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(pending)) {
            throw new APIRuntimeException(ResultCodeEnum.NOT_WORKS_CAN_RECEIVE);
        }
        Boolean slot = pending.stream().map(WorksTransferEntity::getSlot).findFirst().orElse(false);
        Long oriUserId = pending.stream().map(WorksTransferEntity::getOriUserId).findFirst().orElse(null);
        List<Long> worksId = pending.stream().map(WorksTransferEntity::getWorkId).distinct().collect(Collectors.toList());
        List<WorksEntity> worksEntityList = worksMapper.queryIdsAndUserId(worksId, oriUserId);
        if (CollectionUtils.isEmpty(worksEntityList)) {
            throw new APIRuntimeException(ResultCodeEnum.WORK_NOT_EXIST);
        }
        List<SlotEntity> slotEntityList = new ArrayList<>();
        Date date = new Date();
        List<UserWorksEntity> userWorksEntities = userWorksMapper.queryByInWorksAndTypeAndSource(worksId, oriUserId, UserWorksSourceEnum.SELF.getCode(), UserWorksTypeEnum.MINE.getCode());
        this.transferWorks(userProfile, userProfile, slot, date, worksId, oriUserId, worksEntityList, slotEntityList, userWorksEntities);
        if (!slot) {
            slotMapper.batchDeleteByWorksId(worksId, worksId.size());
        }
        if (CollectionUtils.isNotEmpty(slotEntityList)) {
            slotMapper.saveAll(slotEntityList);
        }
        if (CollectionUtils.isNotEmpty(worksEntityList)) {
            worksRepository.saveAll(worksEntityList);
        }
        if (CollectionUtils.isNotEmpty(userWorksEntities)) {
            userWorksRepository.saveAll(userWorksEntities);
        }
        pending.forEach(e -> {
            e.setStatus(WorkTransferStatusEnum.FINISH.getCode());
            e.setOperator(userProfile.getMobile());
            e.setUpdatedAt(date);
        });
        worksTransferRepository.saveAll(pending);
        return new WorkTransferReceiveResponse();
    }

    /**
     * 转移作品
     *
     * @param userProfile       用户信息
     * @param targetUser        目标用户
     * @param slot              是否带坑位
     * @param date              操作时间
     * @param worksId           作品id集合
     * @param oriUserId         原用户id
     * @param worksEntityList   作品实体集合
     * @param slotEntityList    坑位实体集合
     * @param userWorksEntities 用户作品集合
     */
    private void transferWorks(UserProfile userProfile, UserProfile targetUser, boolean slot, Date date, List<Long> worksId, Long oriUserId,
                               List<WorksEntity> worksEntityList, List<SlotEntity> slotEntityList, List<UserWorksEntity> userWorksEntities) {
        if (!slot) {
            // 不赠送坑位校验目标用户的坑位是否足够
            UserRightsResponse rightsResponse = userService.userRights(targetUser, ComboConfigItemEnum.SLOT.getItem());
            if (Objects.isNull(rightsResponse) || rightsResponse.getAvailableSlotNumber() < worksEntityList.size()) {
                throw new APIRuntimeException(ResultCodeEnum.TARGET_USER_SLOT_LIMIT);
            }
            // 迁移操作
            worksEntityList.forEach(e -> {
                e.setUserId(targetUser.getId());
                e.setUpdatedAt(date);
                e.setOperator(userProfile.getMobile());
                // 建新坑位
                slotEntityList.add(slotService.buildSlot(null, targetUser, date, e.getId()));
            });
        } else {
            worksEntityList.forEach(e -> {
                e.setUserId(targetUser.getId());
                e.setUpdatedAt(date);
                e.setOperator(userProfile.getMobile());
            });
            List<SlotEntity> slotEntities = slotMapper.queryByWorkIdsAndUserId(worksId, oriUserId);
            if (CollectionUtils.isNotEmpty(slotEntities)) {
                slotEntities.forEach(e -> {
                    e.setUserId(targetUser.getId());
                    e.setUpdatedAt(date);
                    e.setStatus(SlotStatusEnum.AVAILABLE.getCode());
                    e.setOperator(userProfile.getMobile());
                });
                slotEntityList.addAll(slotEntities);
            }
        }
        if (CollectionUtils.isNotEmpty(userWorksEntities)) {
            userWorksEntities.forEach(e -> {
                e.setUserId(targetUser.getId());
                e.setUpdatedAt(date);
                e.setOperator(userProfile.getMobile());
            });
        }
    }

    private void buildTransferEntity(List<WorksTransferEntity> workTransferEntityList, List<WorksEntity> worksEntityList,
                                     Long batchId, WorkTransferRequest request, WorkTransferMethodEnum workTransferMethodEnum,
                                     UserProfile userProfile, Date date) {
        worksEntityList.forEach(e -> {
            WorksTransferEntity workTransferEntity = new WorksTransferEntity();
            workTransferEntity.setBatchId(batchId);
            workTransferEntity.setMethod(workTransferMethodEnum.getCode());
            if (WorkTransferMethodEnum.MANUAL.equals(workTransferMethodEnum)) {
                workTransferEntity.setStatus(WorkTransferStatusEnum.PENDING.getCode());
            } else {
                workTransferEntity.setStatus(WorkTransferStatusEnum.FINISH.getCode());
            }
            workTransferEntity.setWorkId(e.getId());
            workTransferEntity.setSlot(request.getSlot());
            workTransferEntity.setOriUserId(userProfile.getId());
            workTransferEntity.setTargetUserId(request.getUserId());
            workTransferEntity.setCreator(userProfile.getMobile());
            workTransferEntity.setCreatedAt(date);
            workTransferEntity.setUpdatedAt(date);
            workTransferEntityList.add(workTransferEntity);
        });
    }

    /**
     * 获取识别图地址
     *
     * @param identify 识别图
     * @return 识别图地址
     */
    private String getIdentifyUrl(String identify) {
        if (StringUtils.isBlank(identify)) {
            return null;
        }
        JSONObject newIdentifyObject = JSONObject.parseObject(identify);
        if (newIdentifyObject != null) {
            return newIdentifyObject.getString("linkUrl");
        }
        return null;
    }

    /**
     * 兑换品模板查询
     *
     * @param pageNum  当前页
     * @param pageSize 页记录数
     * @param userId   用户ID
     * @param shelves  是否上架
     * @param name     作品名称
     * @return 响应
     */
    public Result<PageResult<AdminExchangeItemTemplatePageResponse>> worksList(int pageNum, int pageSize, List<Long> userId, Boolean shelves, String name) {
        Page<Object> page = PageHelper.startPage(pageNum, pageSize);
        List<WorksEntity> worksEntities = worksMapper.queryByUserOrShelves(userId, shelves, name);
        if (org.apache.dubbo.common.utils.CollectionUtils.isEmpty(worksEntities)) {
            return Result.success(new PageResult<>());
        }
        List<AdminExchangeItemTemplatePageResponse> exchangeItemTemplateList = ExchangeItemMapstruct.INSTANCE.templateEntitiesToPageResponses(worksEntities);
        PageInfo<AdminExchangeItemTemplatePageResponse> exchangeItemTemplatePageInfo = new PageInfo<>(exchangeItemTemplateList);
        exchangeItemTemplatePageInfo.setPageNum(pageNum);
        exchangeItemTemplatePageInfo.setTotal(page.getTotal());
        return PageUtils.getPageResult(exchangeItemTemplatePageInfo, exchangeItemTemplateList);
    }

    /**
     * 作品上下架
     *
     * @param userProfile 用户信息
     * @param worksId     作品ID
     * @param request     请求
     * @return 响应
     */
    public void shelves(UserProfile userProfile, Long worksId, AdminExchangeItemTemplateShelvesRequest request) {
        Optional<WorksEntity> worksEntityOptional = worksRepository.findById(worksId);
        if (!worksEntityOptional.isPresent()) {
            throw new APIRuntimeException(ResultCodeEnum.WORK_NOT_EXIST);
        }
        Boolean shelves = request.getShelves();
        WorksEntity worksEntity = worksEntityOptional.get();
        if (!worksEntity.getUserId().equals(userProfile.getId())) {
            throw new APIRuntimeException(ResultCodeEnum.NOT_SHELVES_PERMISSIONS);
        }
        worksEntity.setShelves(shelves);
        worksRepository.save(worksEntity);
    }
}
